package com.example.diffservice.algorithm;

import com.example.diffservice.algorithm.base.common.Global;
import com.example.diffservice.algorithm.base.links.FileInnerLinksGenerator;
import com.example.diffservice.algorithm.base.links.FileOuterLinksGenerator;
import com.example.diffservice.algorithm.base.links.TotalFileLinks;
import com.example.diffservice.algorithm.base.links.similarity.TreeDistance;
import com.example.diffservice.algorithm.base.miningchangeentity.ChangeEntityData;
import com.example.diffservice.algorithm.base.preprocessingfile.data.FileOutputLog;
import com.example.diffservice.algorithm.entity.CommitFile;
import com.example.diffservice.algorithm.entity.FilePairData;
import com.example.diffservice.algorithm.entity.Meta;
import com.github.gumtreediff.tree.Tree;
import lombok.extern.slf4j.Slf4j;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author DestinyCc
 * @date 2022/4/2
 */
@Slf4j
public class Generator {
    private List<FilePairData> filePairDataList = new ArrayList<>();

    private Map<String, ChangeEntityData> fileChangeEntityData = new HashMap<>();

    private Core core;

    private Meta mete;

    public Generator(Meta meta){
        this.mete = meta;
        core = new Core();
        core.mFileOutputLog = new FileOutputLog(meta.getOutputDir(), meta.getAuthor());
        core.mFileOutputLog.setCommitId(meta.getCommitHash(), meta.getParents());
        List<CommitFile> commitFiles = meta.getFiles();
        for (int i = 0; i < commitFiles.size(); i++) {
            CommitFile file = commitFiles.get(i);
            if (file.getDiffPath() == null){
                continue;
            }
            String fullFilename = file.getFileName();
            int index = fullFilename.lastIndexOf("/");
            String filename = fullFilename.substring(index+1);
            String prevFilePath = file.getPrevFilePath();
            String currFilePath = file.getCurrFilePath();
            String parentCommit = file.getParentCommit();
            String basePath = meta.getOutputDir();
            byte[] prevBytes = null;
            byte[] currBytes = null;
            try{
                if (prevFilePath != null){
                    prevBytes = Files.readAllBytes(Paths.get(basePath + "/" + prevFilePath));
                }
                if (currFilePath != null){
                    currBytes = Files.readAllBytes(Paths.get(basePath + "/" + currFilePath));
                }
            }catch (Exception e){
                e.printStackTrace();
            }
//            FilePairData fp = new FilePairData(prevBytes, currBytes,
//                    basePath + "/" + prevFilePath,
//                    basePath + "/" + currFilePath,
//                    filename);
            FilePairData fp = new FilePairData(prevBytes, currBytes,
                    basePath + "/" + prevFilePath,
                    basePath + "/" + currFilePath,
                    fullFilename);
            fp.setParentCommit(parentCommit);
            filePairDataList.add(fp);
        }
    }

    public void generateResult(){
        String absolutePath = this.mete.getOutputDir();
        Global.changeEntityFileNameMap = new HashMap<>();
        for (FilePairData fp : filePairDataList) {
            Global.parentCommit = fp.getParentCommit();
            System.out.println(fp.getFilename());
            Global.fileName = fp.getFilename();
            if (fp.getPrev() == null && fp.getCurr() == null) {
                continue;
            }
            if (fp.getPrev() == null) {
                this.core.dooAddFile(fp.getFilename(), fp.getCurr(), absolutePath);
            } else if (fp.getCurr() == null) {
                this.core.dooRemoveFile(fp.getFilename(), fp.getPrev(), absolutePath);
            } else {
                this.core.dooDiffFile(fp.getFilename(), fp.getPrev(), fp.getCurr(), absolutePath);
            }
            this.fileChangeEntityData.put(fp.getParentCommit() + "@@@" + this.core.changeEntityData.fileName, this.core.changeEntityData);
        }
        List<String> fileNames = new ArrayList<>(this.fileChangeEntityData.keySet());
        TotalFileLinks totalFileLinks = new TotalFileLinks();
        for (int i = 0; i < fileNames.size(); i++) {
            String fileNameA = fileNames.get(i);
            ChangeEntityData cedA = this.fileChangeEntityData.get(fileNameA);
            Global.ced = cedA;
            FileInnerLinksGenerator associationGenerator = new FileInnerLinksGenerator(cedA);
            associationGenerator.generateFile();
            totalFileLinks.addEntry(fileNameA, cedA.mLinks);
        }
        for (int i = 0; i < fileNames.size(); i++) {
            String fileNameA = fileNames.get(i);
            //存在fileName为null的情况，处理fileName为何为null的情况之后再做处理。
            ChangeEntityData cedA = this.fileChangeEntityData.get(fileNameA);
            FileOuterLinksGenerator fileOuterLinksGenerator = new FileOuterLinksGenerator();
            for (int j = i + 1; j < fileNames.size(); j++) {
                String fileNameB = fileNames.get(j);
                //存在fileName为null的情况
                ChangeEntityData cedB = this.fileChangeEntityData.get(fileNameB);
                fileOuterLinksGenerator.generateOutsideAssociation(cedA, cedB);
                totalFileLinks.addFile2FileAssos(fileNameA, fileNameB, fileOuterLinksGenerator.mAssos);
            }
        }
        new FileOuterLinksGenerator().checkSimilarity(this.fileChangeEntityData,totalFileLinks);
        core.mFileOutputLog.writeLinkJson(totalFileLinks.toAssoJSonString());
        System.out.println(totalFileLinks.toConsoleString());
        fileChangeEntityData.clear();
    }

    public float distance(Tree tree1, Tree tree2) {
        TreeDistance treeDistance = new TreeDistance(tree1, tree2);
        float distance = treeDistance.calculateTreeDistance();
        return distance;
    }
}
